﻿#region Creditos

/* Autor: Edilberto Sánchez Forero	*/
/* Email: esanchezfo@gmail.com		*/
/* Licencia: LGPL, Software Libre	*/
/* País: Colombia, Bogotá			*/

#endregion

#region Librerias

using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Collections.Specialized;
using System.Collections.Generic;

using ESF.General;
using ESF.Object.XmlRepresenter.Extensions;
using ESF.Object.XmlRepresenter.Extensions.Description;
using ESF.General.Text;

#endregion


namespace ESF.Object.XmlRepresenter
{
	public sealed class XmlMember : IEquatable<XmlMember>
	{
		#region Constantes
		
		public const byte NUMERIC_MAXLENGTH = 65;
		
		public const byte ALIAS_MAXLENGTH = 7;
		
		#endregion
		
	
		#region Miembros
		
		private string _Name;
		private string _Alias;
		private string _DefaultValue;
		private string _FromSet;
		private string _CustomType;
			
		private readonly XmlObject _Object;
				
		private Regex _RegEx;
		
		private ValueTypes? _Type;	
		
		private RegExTypes? _RegExType;
		
		private ushort? _Length;
		private ushort? _MinLength;
		private ushort? _MaxLength;
		
		private bool _Required;
		private bool _Encrypted;
		private bool _IsForShortQuery;
		
		private byte? _NumericPrecision;
		private byte? _NumericScale;
		
		private int _LineNumber;
		private int _LinePosition;
		
		private List<XmlMemberDescription> _Descriptions;
		
		private bool _DescriptionsClosed;
		
		private List<string> _DescriptionISOLanguageCodes;
		
		#endregion
				
		
		#region Propiedades
		
		public bool IsCustom { get { return this._Type == ValueTypes.Custom; } }
		
		public bool IsBasic { get { return this._Type != ValueTypes.Custom; } }
		
		public XmlObject Object { get { return this._Object; } }
		
		public string Name { get { return this._Name; } }
		
		public string NameXML 
		{
			get
			{
				if (!this.IsCustom)
				{
					return this._Name [0].ToString ().ToLower () + this._Name.Substring (1, this._Name.Length - 1);
				}
				
				return this._Name;
			}
		} 
				
		public string FullName { get { return string.Format ("{0}.{1}.{2}", this._Object.SetName, this._Object.Name, this._Name); } }
				
		public string Alias 
		{
			get 
			{
				if (string.IsNullOrEmpty (this._Alias))
				{
					return this._Name; 
				}
				
				return this._Alias;
			}
		}		
		
		public string DefaultValue { get { return this._DefaultValue; } }
		
		public string FromSet { get { return this._FromSet; } }
		
		public string CustomType { get { return this._CustomType; } }
		
		public Regex RegEx 
		{
			get 
			{
				string strRegEx;
				
				
				if (this.IsCustom) { return null; }
				
				strRegEx = null;
			
				if (this._RegEx != null)
				{
					return this._RegEx;
				}
				
				#region Por Tipo
				
				switch (this._Type)
				{
					case ValueTypes.SByte:
						strRegEx = TextHelper.REGEX_SByte;
						break;
				
					case ValueTypes.Byte:
						strRegEx = TextHelper.REGEX_Byte;
						break;
						
					case ValueTypes.Date:
						strRegEx = TextHelper.REGEX_Date;
						break;
						
					case ValueTypes.DateTime:
						strRegEx = TextHelper.REGEX_DateTime;	 
						break;
						
					case ValueTypes.Decimal:
						strRegEx = TextHelper.REGEX_Decimal;
						break;
						
					case ValueTypes.Double:
						strRegEx = TextHelper.REGEX_Double;
						break;
						
					case ValueTypes.Float:
						strRegEx = TextHelper.REGEX_Float;
						break;
						
					case ValueTypes.Int16:
						strRegEx = TextHelper.REGEX_Int16;
						break;
						
					case ValueTypes.Int32:
						strRegEx = TextHelper.REGEX_Int32;
						break;
						
					case ValueTypes.Int64:
						strRegEx = TextHelper.REGEX_Int64;
						break;
						
					case ValueTypes.UInt16:
						strRegEx = TextHelper.REGEX_UInt16;
						break;
						
					case ValueTypes.UInt32:
						strRegEx = TextHelper.REGEX_UInt32;
						break;
						
					case ValueTypes.UInt64:
						strRegEx = TextHelper.REGEX_UInt64;
						break;
				}
				
				if (!string.IsNullOrEmpty (strRegEx))
				{
					return new Regex (strRegEx, RegexOptions.IgnoreCase); 
				}
				
				#endregion
				
				#region Por categoria de expresion regular
				
				switch (this._RegExType)
				{
					case RegExTypes.Alfa:
						strRegEx = TextHelper.REGEX_Alfa;
						break;
					
					case RegExTypes.AlfaNumeric:
						strRegEx = TextHelper.REGEX_AlfaNumeric;
						break;
						
					case RegExTypes.Decimal:
						strRegEx = TextHelper.REGEX_Decimal;
						break;
						
					case RegExTypes.Email:
						strRegEx = TextHelper.REGEX_Email;
						break;
						
					case RegExTypes.SByte:
						strRegEx = TextHelper.REGEX_SByte;
						break;
						
					case RegExTypes.Byte:
						strRegEx = TextHelper.REGEX_Byte;
						break;
						
					case RegExTypes.Int16:
						strRegEx = TextHelper.REGEX_Int16;
						break;
						
					case RegExTypes.UInt16:
						strRegEx = TextHelper.REGEX_UInt16;
						break;
						
					case RegExTypes.Int32:
						strRegEx = TextHelper.REGEX_Int32;
						break;
						
					case RegExTypes.UInt32:
						strRegEx = TextHelper.REGEX_UInt32;
						break;
						
					case RegExTypes.Int64:
						strRegEx = TextHelper.REGEX_Int64;
						break;
						
					case RegExTypes.UInt64:
						strRegEx = TextHelper.REGEX_UInt64;
						break;
						
					case RegExTypes.Double:
						strRegEx = TextHelper.REGEX_Double;
						break;
						
					case RegExTypes.Float:
						strRegEx = TextHelper.REGEX_Float;
						break;
						
					case RegExTypes.Percentage:
						strRegEx = TextHelper.REGEX_Percentage;
						break;
						
					case RegExTypes.URL:
						strRegEx =TextHelper.REGEX_URL;
						break;
						
					case RegExTypes.Date:
						strRegEx = TextHelper.REGEX_Date;
						break;
						
					case RegExTypes.DateTime:
						strRegEx = TextHelper.REGEX_DateTime;
						break;
						
					default:
						return null;
				}
				
				#endregion
						
				return new Regex (strRegEx, RegexOptions.IgnoreCase); 
			} 
		}
		
		public ValueTypes? Type { get { return this._Type; } }
		
		public RegExTypes? RegExType { get { return this._RegExType; } }
		
		
		public ushort? Length { get { return this._Length; } }
		
		public ushort? MinLength { get { return this._MinLength; } }
		
		public bool Required 
		{
			get 
			{
				return this._Required; 
			}
		}
		
		
		public bool Encrypted 
		{
			get
			{
				return this._Encrypted; 
			}
		}
		
		
		public bool IsForShortQuery 
		{
			get 
			{
				return this._IsForShortQuery;
			}
		}
		
		
		public bool UsesNullableType 
		{
			get
			{
				if (this.Type == ValueTypes.Custom || 
					this.Type == ValueTypes.String)
				{
					return false;
				}
				
				return !this.Required;
			}
		}
		
		
		public byte? NumericPrecision { get { return this._NumericPrecision; } }
		
		public byte? NumericScale { get { return this._NumericScale; } }
		
		
		public ushort? ApplicableMaxLength
		{
			get
			{
				if (this._Length != null) { return this._Length.Value; }
				if (this._MaxLength != null) { return this._MaxLength.Value; }
				
				return TextHelper.GetMaxLength (this._Type);
			}
		}
		
		public string ApplicableDotNETType
		{
			get
			{
				string strResult;
				
			
				if (this.Type == ValueTypes.Custom)
				{
					return this.CustomType;
				}
				else if (this.Type == ValueTypes.Date)
				{
					return "DateTime?";
				}
				else if (this.Type == ValueTypes.Bool)
				{
					return "bool?";
				}
				
				strResult = this.Type.ToString ();
				
				if (this.Type != ValueTypes.String) { strResult += "?"; } 
				
				return strResult;
			}
		}
		
		public string ApplicableFromSet
		{
			get
			{
				if (string.IsNullOrEmpty (this._FromSet))
				{
					return this.Object.SetName;
				}
				
				return this._FromSet;
			}
		}
		
		
		public List<string> DescriptionsISOLanguageCodes
		{
			get
			{
				return this._DescriptionISOLanguageCodes;
			}
		}
		
		
		public List<XmlMemberDescription> Descriptions
		{
			get
			{
				if (!this._DescriptionsClosed)
				{
					throw new GenericException (GenericException.CRI_OBJECTCOLLECTION_MUSTBECLOSED, string.Format ("El listado de descripciones del miembro {0} no ha sido cerrado", this._Name));
				}
				
				return this._Descriptions;
			}
		}
		
		#endregion
		
		
		#region Constructor
				
		public XmlMember (ref XmlObject pobjObject, ref XmlTextReader pxmlReader)
		{
			XmlHelper xmlEx;
			
			object objTemp;
			
			
			this._Name = pxmlReader.Name;
			
			this._Object = pobjObject;
			
			this._Encrypted = false;
			this._Required = false;
			
			this._LineNumber = pxmlReader.LineNumber;
			this._LinePosition = pxmlReader.LinePosition;
			
			if (Array.IndexOf<string> (Enum.GetNames (typeof (ValueTypes)), this._Name) != -1)
			{
				throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, 
					string.Format ("{0}: El nombre del miembro {1} no puede ser uno de los tipos predeterminados de .NET en ROSSOX", 
						this.FullName, this.Name), 
					pxmlReader.LineNumber, pxmlReader.LinePosition);
			}
			
			this._DescriptionISOLanguageCodes = new List<string> ();
			
			xmlEx = new XmlHelper ();
			xmlEx.ObjectName = this.FullName;
			xmlEx.EvaluatedNodeReader = pxmlReader;
			
			#region Lectura de Atributos
			
			objTemp = xmlEx.GetAttributeEnum (false, "type", "Tipo", typeof (ValueTypes), true);
			
			if (objTemp == null) 
			{
				this._Type = ValueTypes.Custom; 
				
				this._CustomType = this._Name;
			}
			else { this._Type = (ValueTypes) objTemp; }
			
			
			this._Alias = xmlEx.GetAttributeValue (false, "alias", "Alias", XmlMember.ALIAS_MAXLENGTH);
			
			this._RegEx = xmlEx.GetAttributeValueRegEx (false, "regEx", "Expresión Regular");
			
			
			objTemp = xmlEx.GetAttributeEnum (false, "regExType", "Tipo de Expresión Regular", typeof (RegExTypes));
			
			if (objTemp != null) { this._RegExType = (RegExTypes) objTemp; }
			
			
			this._Length = xmlEx.GetAttributeValueUInt16 (false, "length", "Longitud");
			
			this._MinLength = xmlEx.GetAttributeValueUInt16 (false, "minLength", "Longitud Mínima");
			
			this._MaxLength = xmlEx.GetAttributeValueUInt16 (false, "maxLength", "Longitud Maxima");
			
			this._Required = xmlEx.GetAttributeValueBool (false, "required", "Requerido", false).Value;
			
			this._DefaultValue = xmlEx.GetAttributeValue (false, "defaultValue", "Valor por defecto");
			
			this._Encrypted = xmlEx.GetAttributeValueBool (false, "encrypted", "Encriptado", false).Value;
			
			this._FromSet = xmlEx.GetAttributeValue (false, "fromSet", "Nombre del Origen externo");
			
			this._NumericPrecision = xmlEx.GetAttributeValueByte (false, "numericPrecision", "Precisión Numérica");
			
			this._NumericScale = xmlEx.GetAttributeValueByte (false, "numericScale", "Escala Númerica");
			
			this._IsForShortQuery = xmlEx.GetAttributeValueBool (false, "isForShortQuery", "Indicador de Campo de Consulta Resumida", false).Value;
			
			#endregion
						
			this.Check (xmlEx.Validate);
			
			this._Descriptions = new List<XmlMemberDescription> ();
		}
		
		#endregion
		
		
		#region Funciones
	
		private void Check (Validate pobjVal)
		{
			ushort? shrTempMaxValue;
			
			Regex objRegEx;
			
			
			shrTempMaxValue = TextHelper.GetMaxLength (this._Type);
			objRegEx = this.RegEx;
		
		
			#region Revision del Nombre y Tipo
			
			if (this._CustomType == this._Object.Name)
			{
				throw new GenericException (GenericException.CRI_INVALID_REFERENCE, 
					string.Format ("{0}: Un miembro de un objeto no puede contener miembros del mismo tipo del objeto, las relaciones recurrentes deben ser expresadas mediante otras entidades", this.FullName),
					this._LineNumber, this._LinePosition);
			}
			
			#endregion
		
			#region Revision del Alias
			
			if (this._Type == ValueTypes.Custom && string.IsNullOrEmpty (this._Alias))
			{
				throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, string.Format ("El atributo 'alias' es requerido debido al tipo de dato no básico {0} pues su tipo no corresponde a uno básico", this._CustomType), this._LineNumber, this._LinePosition);
			}
			
			#endregion
		
			#region Revision de longitudes
			
			// Si esta definida la longitud no deben estar ni la longitud minima ni la maxima definidas
			if (this.Length > 0)
			{
				if (this._MinLength > 0)
				{	
					throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, "'length' y 'minLength' no pueden estar definidos al mismo tiempo", this._LineNumber, this._LinePosition);
				}
				
				if (this._MaxLength > 0)
				{
					throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, "'length' y 'maxLength' no pueden estar definidos al mismo tiempo", this._LineNumber, this._LinePosition);
				}
			}
			
			// Se revisa que las longitudes sean coherentes
			if (this._MinLength > 0 && this._MaxLength > 0 && this.MinLength > this._MaxLength)
			{
				throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, "El valor debe ser menor que el valor de 'maxLength'", this._LineNumber, this._LinePosition);
			}
			
			if (this._Length != null)
			{	
				if (shrTempMaxValue != null && this._Length > shrTempMaxValue)
				{
					throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, string.Format ("El valor de 'length' {0}, por el tipo de dato {1} no puede superar los {2} carateres", this._Length, this._Type, shrTempMaxValue));	
				}
			}
			
			if (this._MaxLength != null)
			{
				if (shrTempMaxValue != null && this._MaxLength > shrTempMaxValue)
				{
					throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, string.Format ("El valor de 'length' {0}, por el tipo de dato {1} no puede superar los {2} carateres", this._MaxLength, this._Type, shrTempMaxValue));	
				}
			}
			
			#endregion

			#region Revision de Tipos
			
			bool blnValue;
			
			byte bytValue;
			
			DateTime dtmValue;
			
			decimal decValue;
			
			double dblValue;
			
			float fltValue;
			
			int intValue;
			
			long lngValue;
			
			short shrValue;
			
			uint uintValue;
			
			ulong ulngValue;
			
			ushort ushrValue;
			
						
			if (!string.IsNullOrEmpty (this.DefaultValue))
			{
				// Se revisa si el tipo de dato del defaultValue corresponde al tipo de dato del miembro
				switch (this.Type)
				{
					case ValueTypes.Bool:
						if (!bool.TryParse (this.DefaultValue, out blnValue))
						{
							throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, "El valor por defecto debe ser un valor del tipo bool", this._LineNumber, this._LinePosition);
						}
						break;
						
					case ValueTypes.Byte:
						if (!byte.TryParse (this.DefaultValue, out bytValue))
						{
							throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, "El valor por defecto debe ser un valor del tipo byte", this._LineNumber, this._LinePosition);
						}
						break;
						
					case ValueTypes.Date:
						if (!DateTime.TryParse (this.DefaultValue, out dtmValue))
						{
							throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, "El valor por defecto debe ser un valor del tipo DateTime", this._LineNumber, this._LinePosition);
						}
						break;
						
					case ValueTypes.DateTime:
						if (!DateTime.TryParse (this.DefaultValue, out dtmValue))
						{
							throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, "El valor por defecto debe ser un valor del tipo DateTime", this._LineNumber, this._LinePosition);
						}
						break;
						
					case ValueTypes.Decimal:
						if (!decimal.TryParse (this.DefaultValue, out decValue))
						{
							throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, "El valor por defecto debe ser un valor del tipo decimal", this._LineNumber, this._LinePosition);
						}
						break;
					
					case ValueTypes.Double:
						if (!double.TryParse (this.DefaultValue, out dblValue))
						{	
							throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, "El valor por defecto debe ser un valor del tipo double", this._LineNumber, this._LinePosition);
						}
						break;
						
					case ValueTypes.Float:
						if (!float.TryParse (this.DefaultValue, out fltValue))
						{	
							throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, "El valor por defecto debe ser un valor del tipo float", this._LineNumber, this._LinePosition);
						}
						break;
						
					case ValueTypes.Int32:
						if (!int.TryParse (this.DefaultValue, out intValue))
						{	
							throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, "El valor por defecto debe ser un valor del tipo int", this._LineNumber, this._LinePosition);
						}
						break;
						
					case ValueTypes.Int64:
						if (!long.TryParse (this.DefaultValue, out lngValue))
						{
							throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, "El valor por defecto debe ser un valor del tipo long", this._LineNumber, this._LinePosition);
						}
						break;
						
					case ValueTypes.Int16:
						if (!short.TryParse (this.DefaultValue, out shrValue))
						{
							throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, "El valor por defecto debe ser un valor del tipo short", this._LineNumber, this._LinePosition);
						}
						break;
						
					case ValueTypes.UInt32:
						if (!uint.TryParse (this.DefaultValue, out uintValue))
						{
							throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, "El valor por defecto debe ser un valor del tipo uint", this._LineNumber, this._LinePosition);
						}
						break;	
						
					case ValueTypes.UInt64:
						if (!ulong.TryParse (this.DefaultValue, out ulngValue))
						{	
							throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, "El valor por defecto debe ser un valor del tipo ulong", this._LineNumber, this._LinePosition);
						}
						break;
						
					case ValueTypes.UInt16:
						if (!ushort.TryParse (this.DefaultValue, out ushrValue))
						{
							throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, "El valor por defecto debe ser un valor del tipo ushort", this._LineNumber, this._LinePosition);
						}
						break;
				}
			}
			
			#endregion
			
			#region Revision de Expresiones Regulares
			
			if (this._Type == ValueTypes.Custom && this._RegEx != null)
			{
				throw new GenericException (GenericException.CRI_INVALID_DEFINITION, "No puede definir una Expresion Regular (regEx) para un tipo Custom (a la medida), solo para tipos basicos es valido por ejemplo cadenas (string)", this._LineNumber, this._LinePosition);
			}
			
			if (this._Type == ValueTypes.Custom && this._RegExType != null)
			{
				throw new GenericException (GenericException.CRI_INVALID_DEFINITION, "No puede definir un Tipo de Expresion Regular (regExType) para un tipo Custom (a la medida), solo para tipos basicos es valido por ejemplo cadenas (string)", this._LineNumber, this._LinePosition);
			}
			
			if (objRegEx != null)
			{
				// Se revisa si el tipo es una cadena para que el defaultValue corresponda con la expresion regular si la hay			
				if (!string.IsNullOrEmpty (this.DefaultValue))
				{
					if (!objRegEx.IsMatch (this.DefaultValue))
					{
						throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, "El valor por defecto no supera la validación definida en 'regEx'", this._LineNumber, this._LinePosition);
					}
				}
			}
			
			pobjVal.VerifyRegExTypeMaxLength (this._RegExType, "Length", this._Length);
			pobjVal.VerifyRegExTypeMaxLength (this._RegExType, "MaxLength", this._MaxLength);
			
			#endregion
			
			#region Revision de parametros extra
			
			ushort shrTemp;
			
			
			if (this._NumericPrecision != null && 
				this._Type != ValueTypes.Byte &&
				this._Type != ValueTypes.Decimal && 
				this._Type != ValueTypes.Float &&
				this._Type != ValueTypes.Double && 
				this._Type != ValueTypes.Int32 &&
				this._Type != ValueTypes.Int64 &&
				this._Type != ValueTypes.Int16 &&
				this._Type != ValueTypes.UInt32 &&
				this._Type != ValueTypes.UInt64 &&
				this._Type != ValueTypes.UInt16)
			{			
				throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, string.Format ("El atributo 'numericPrecision' no es valido para {0} de tipo {1}", this._Name, this._Type), this._LineNumber, this._LinePosition);
			}
			
			if (this._NumericScale != null && 
				this._Type != ValueTypes.Decimal && 
				this._Type != ValueTypes.Float &&
				this._Type != ValueTypes.Double)
			{
				throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, string.Format ("El atributo 'numericPrecision' no es valido para {0} de tipo {1}", this._Name, this._Type), this._LineNumber, this._LinePosition);
			}
			
			if (this._NumericScale != null && this._NumericPrecision == null)
			{			
				throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, "El atributo 'numericScale' requiere el atributo 'numericPrecision'", this._LineNumber, this._LinePosition);
			}
			
			if (this._NumericPrecision != null)
			{
				shrTemp = this._NumericPrecision.Value;
				
				if (this._NumericScale != null) { shrTemp += this._NumericScale.Value; }
				
				if (shrTemp > XmlMember.NUMERIC_MAXLENGTH)
				{
					throw new GenericException (GenericException.CRI_INVALID_ATTRIBUTE, string.Format ("Los atributos 'numericScale' y 'numericPrecision' juntos no pueden superar los {0} digitos pero tienen {1}", XmlMember.NUMERIC_MAXLENGTH, shrTemp), this._LineNumber, this._LinePosition);
				}
			}
			
			#endregion
		}
		
		
		public void ToXml (ref XmlTextWriter pxmlWriter, bool pblnIncludeMemberAttributes)
		{
			pxmlWriter.WriteStartElement (this.Name);
						
			if (pblnIncludeMemberAttributes)
			{
				if (!string.IsNullOrEmpty (this._DefaultValue))
				{
					pxmlWriter.WriteAttributeString ("defaultValue", this._DefaultValue);
				}
				
				if (this._Length > 0)
				{
					pxmlWriter.WriteAttributeString ("length", this._Length.ToString ());
				}
				
				if (this._MaxLength > 0)
				{
					pxmlWriter.WriteAttributeString ("maxLength", this._MaxLength.ToString ());
				}
				
				if (this._MinLength > 0)
				{
					pxmlWriter.WriteAttributeString ("minLength", this._MinLength.ToString ());
				}
				
				if (this._RegExType != null)
				{
					pxmlWriter.WriteAttributeString ("regExType", this._RegExType.Value.ToString ());
				}
				else if (this._RegEx != null)
				{
					pxmlWriter.WriteAttributeString ("regEx", this._RegEx.ToString ());
				}
				
				pxmlWriter.WriteAttributeString ("required", this._Required.ToString ());
				
				if (this._Type != null)
				{
					pxmlWriter.WriteAttributeString ("type", this._Type.Value.ToString ());
				}
				
				pxmlWriter.WriteAttributeString ("encrypted", this._Encrypted.ToString ());
				
				if (!string.IsNullOrEmpty (this._FromSet))
				{
					pxmlWriter.WriteAttributeString ("fromSet", this._FromSet);
				}
				
				if (this._NumericPrecision != null)
				{
					pxmlWriter.WriteAttributeString ("numericPrecision", this._NumericPrecision.Value.ToString ());
				}
				
				if (this._NumericScale != null)
				{
					pxmlWriter.WriteAttributeString ("numericScale", this._NumericScale.Value.ToString ());
				}
			}
			
			pxmlWriter.WriteEndElement ();
		}


		public override string ToString ()
		{
			if (this.Type == ValueTypes.Custom)
			{
				return string.Format ("{0} ({1}/{2})", this.FullName, this.Type, this.CustomType);
			}
			
			return string.Format ("{0} ({1})", this.FullName, this.Type);
		}
		
		#endregion


		#region Gestion de la Descripcion
		
		internal void CloseDescriptions (XmlObjectSetManager pobjObjectSetManager)
		{
			int intI;
			
		
			if (this._DescriptionsClosed) { return; }
			
			for (intI = 0; intI < this._Descriptions.Count; intI ++)
			{
				this._Descriptions [intI] = this.AdjustDescription (this._Descriptions [intI], pobjObjectSetManager);
			}
			
			this._DescriptionsClosed = true;
		}
		
		
		public XmlMemberDescription GetDescription ()
		{
			XmlMemberDescription objDesc;
			
		
			if (!this._DescriptionsClosed)
			{
				throw new GenericException (GenericException.CRI_OBJECTCOLLECTION_MUSTBECLOSED, string.Format ("El listado de descripciones del miembro {0} no ha sido cerrado", this._Name));
			}
			
			objDesc = this._Descriptions.Find (d => d.Default);
			
			if (objDesc == null) { return new XmlMemberDescription (this._Name); }
			
			return objDesc;
		}
		
		
		public XmlMemberDescription GetDescription (string pstrISOLanguageCode)
		{
			XmlMemberDescription objDesc;
			
			
			if (!this._DescriptionsClosed)
			{
				throw new GenericException (GenericException.CRI_OBJECTCOLLECTION_MUSTBECLOSED, string.Format ("El listado de descripciones del miembro {0} no ha sido cerrado", this._Name));
			}
			
			objDesc = this._Descriptions.Find (d => d.ISOLanguageCode == pstrISOLanguageCode);
			
			if (objDesc == null) { return new XmlMemberDescription (this._Name); }
			
			return objDesc;
		}
		
		
		private XmlMemberDescription AdjustDescription (XmlMemberDescription pobjDescription, XmlObjectSetManager pobjObjectSetManager)
		{
			XmlObject objRef;
			XmlObject objExtRef;
		
		
			if (pobjDescription == null)
			{
				return new XmlMemberDescription (this._Name);
			}
			
			if (string.IsNullOrEmpty (pobjDescription.Label))
			{
				if (this.IsCustom)
				{
					pobjObjectSetManager.GetObjectBy (this, out objRef, out objExtRef);
				
					// No es un tipo derivado (array por ejemplo)
					if (objExtRef == null)
					{
						pobjDescription.Label = objRef.GetDescription (pobjDescription.ISOLanguageCode).Label;
					}
					else
					{
						pobjDescription.Label = objExtRef.GetDescription (pobjDescription.ISOLanguageCode).Label;
					}
				}
				else
				{
					pobjDescription.Label = this._Name;
				}
			}
			
			if (string.IsNullOrEmpty (pobjDescription.Description))
			{
				if (this.IsCustom)
				{
					pobjObjectSetManager.GetObjectBy (this, out objRef, out objExtRef);
				
					// No es un tipo derivado (array por ejemplo)
					if (objExtRef == null)
					{
						pobjDescription.Description = objRef.GetDescription (pobjDescription.ISOLanguageCode).Description;
					}
					else
					{
						pobjDescription.Description = objExtRef.GetDescription (pobjDescription.ISOLanguageCode).Description;
					}
				}
			}
			
			return pobjDescription;
		}
		
		
		internal void AddDescription (XmlMemberDescription pobjDescription)
		{
			if (this._DescriptionsClosed)
			{
				throw new GenericException (GenericException.CRI_OBJECTCOLLECTION_CLOSED, string.Format ("El listado de descripciones del miembro {0} esta cerrado, no admite nuevas", this._Name));
			}
		
			if (pobjDescription.MemberName != this.Name)
			{
				throw new GenericException (GenericException.CRI_INVALID_ELEMENT, string.Format ("La descripcion del miembro {0} que esta agregando no corresponde a este miembro {1}", pobjDescription.MemberName, this.Name));
			}
		
			this._Descriptions.Add (pobjDescription);
			
			if (!this._DescriptionISOLanguageCodes.Contains (pobjDescription.ISOLanguageCode))
			{
				this._DescriptionISOLanguageCodes.Add (pobjDescription.ISOLanguageCode);
			}
		}
		
		#endregion


		#region IEquatable<XmlMember> Members

		public bool Equals (XmlMember pobjMember)
		{
			if (this.Object == pobjMember.Object &&
				this.Name == pobjMember.Name &&				
				this.Type == pobjMember.Type)
			{
				if (string.IsNullOrEmpty (this.Alias)) { return true; }
				
				return (this.Alias == pobjMember.Alias);
			};
						
			return false;
		}

		#endregion
	}
}